package game

import (
	"base"
	"gate_server/chanrpc"
	"gate_server/logic/game/api"
	"gate_server/logic/game/event"
	. "gate_server/logic/internal"
	"gate_server/logic/player/api"
	"gate_server/logic/player/event"
	"gate_server/model"
	"leaf/gate"
	"proto/base"
	"proto/server/game"
	"proto/server/gate"
)

type gameSystem struct {
	servers map[base.AgentId]*model.GameServer
}

var system = new(gameSystem)

func init() {
	system.servers = make(map[base.AgentId]*model.GameServer)
}

func init() {
	game_api.Logout = system.Logout
	game_api.Request = system.Request
}

func init() {
	Event.Listen(player_event.OnLogin, system.onLogin)
	Event.Listen(player_event.OnLogout, system.onLogout)
}

func init() {
	Skeleton.AfterInit(func() {
		Skeleton.RegisterChanRPC(chanrpc.NewGameAgent, system.newAgent)
		Skeleton.RegisterChanRPC(chanrpc.CloseGameAgent, system.closeAgent)
	})
}

func (*gameSystem) Logout(player *model.Player) {
	server, exists := system.servers[player.GameId()]
	if !exists {
		return
	}

	msg := new(proto_server_game.Logout)
	msg.PlayerId = player.Id().String()
	server.WriteMsg(msg)
}

func (*gameSystem) Request(playerId base.PlayerId, name string, data []byte) {
	player, exists := player_api.Get(playerId)
	if !exists {
		base.Logger.Release("game system send request failed, not player %s", playerId)
		return
	}

	serverId := player.GameId()
	server, exists := system.servers[serverId]
	if !exists {
		base.Logger.Release("game system send request failed, not server %v", serverId)
		return
	}

	request := new(proto_server_gate.Request)
	request.PlayerId = playerId.String()
	request.Name = name
	request.Data = data
	server.WriteMsg(request)
}

func (*gameSystem) onLogin(msg *proto_server_gate.OnLogin, player *model.Player) {
	if msg.Code != proto_base.ErrorCode_Success {
		return
	}

	serverId := base.AgentId(msg.GameId)
	server, exists := system.servers[serverId]
	if !exists {
		return
	}

	server.AddPlayer(player.Id())
}

func (*gameSystem) onLogout(player *model.Player, reason proto_base.LogoutReason) {
	serverId := player.GameId()
	if server, exists := system.servers[serverId]; exists {
		server.RemovePlayer(player.Id())
	}
}

func (*gameSystem) newAgent(args []interface{}) {
	agent := args[0].(gate.Agent)
	base.Logger.Debug("new game server agent, remote address: %s, local address: %s", agent.RemoteAddr(), agent.LocalAddr())

	server := model.NewGameServer(agent)
	agent.SetUserData(server)
}

func (*gameSystem) closeAgent(args []interface{}) {
	agent := args[0].(gate.Agent)
	base.Logger.Debug("close game server agent, remote address: %s, local address: %s", agent.RemoteAddr(), agent.LocalAddr())

	server := agent.UserData().(*model.GameServer)
	players := make([]base.PlayerId, 0, len(server.Players()))
	for playerId := range server.Players() {
		players = append(players, playerId)
	}

	agent.SetUserData(nil)

	Event.Notify(game_event.OnClose, players)
}
